/*
 Copyright (c) 2009, Ivan Popivanov
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:

 * Redistributions of source code must retain the above copyright
 notice, this list of conditions and the following disclaimer.

 * Redistributions in binary form must reproduce the above copyright
 notice, this list of conditions and the following disclaimer in the
 documentation and/or other materials provided with the distribution.

 * Neither the name of StockCandles nor the names of its contributors
 may be used to endorse or promote products derived from this software
 without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <QtGui/QTextBlock>
#include <QtCore/QFile>
#include <QtCore/QTextStream>

#include "Console.h"
#include "Settings.h"
#include "AppEngn.h"

Console::Console(QWidget *parent /* = NULL */)
       : QTextEdit(parent)
{
   // StrongFocus to get keyboard input
   setFocusPolicy(Qt::StrongFocus);

   // The best console font!
   QFont courier("Courier New", 10);
   setFont(courier);

   // Register the application engine with the scripting engine
   QScriptValue objectValue = scriptEngine.newQObject(AppEngn::instance());
   scriptEngine.globalObject().setProperty("engn", objectValue);

   // Load the history
   loadHistory();
   historyId = -1;

   // The history is written out when the settings are saved
   QObject::connect(Settings::instance(), SIGNAL(closing()), this, SLOT(saveHistory()));
}

void
Console::loadHistory()
{
   QFile file(Settings::instance()->getHistoryPath());
   if(file.open(QFile::ReadOnly))
   {
      QTextStream ts(&file);
      for(QString line = ts.readLine(); !line.isNull(); line = ts.readLine())
      {
         if(!history.contains(line))
         {
            history.append(line);
         }
      }
      file.close();
   }
}

void
Console::saveHistory()
{
   QFile file(Settings::instance()->getHistoryPath());
   if(file.open(QFile::WriteOnly | QFile::Truncate))
   {
      QTextStream ts(&file);
      // Only the first occurence for repetitive commands
      for(int i = 0; i < history.size(); ++i)
      {
         int j;
         for(j = 0; j < i && history.at(i) != history.at(j); ++j)
         {
         }
         if(j == i)
         {
            ts << history.at(i) << endl;
         }
      }
      file.close();
   }
}

static inline void
deleteCurrentLine(QTextCursor &cursor)
{
   cursor.beginEditBlock();
   cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
   cursor.movePosition(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
   cursor.removeSelectedText();
   cursor.endEditBlock();
}

void
Console::keyPressEvent(QKeyEvent *ke)
{
   switch(ke->key())
   {
   case Qt::Key_Return:
      {
         // Get the cursor
         QTextCursor tc = textCursor();

         // Move to the end of the line
         tc.movePosition(QTextCursor::EndOfLine, QTextCursor::MoveAnchor);

         // Process the command
         QString result = processCommand(tc.block().text());

         // Add a new paragraph and output the result
         tc.insertBlock();
         tc.insertText(result);

         // Move to the end of line, add a new paragraph and go to the next line
         tc.movePosition(QTextCursor::EndOfLine, QTextCursor::MoveAnchor);
         tc.insertBlock();

         // Set the component cursor to our cursor
         setTextCursor(tc);
      }
      break;

   case Qt::Key_Backspace:
      if(!textCursor().atBlockStart())
      {
         QTextEdit::keyPressEvent(ke);
      }
      break;

   case Qt::Key_Up:
      if(history.size() > 0)
      {
         QTextCursor cursor = textCursor();

         // Delete the current line
         deleteCurrentLine(cursor);

         // Add a line from the history
         if(historyId < 0)
         {
            historyId = 0;
         }
         else
         {
            historyId = (historyId + 1) % history.size();
         }

         cursor.insertText(history[historyId]);
      }
      break;

   case Qt::Key_Down:
      if(history.size() > 0)
      {
         QTextCursor tc = textCursor();

         // Delete the current line
         deleteCurrentLine(tc);

         // Add a line from the history
         if(historyId < 0)
         {
            historyId = 0;
         }
         else
         {
            --historyId;
            if(historyId < 0) historyId = history.size() - 1;
         }

         tc.insertText(history[historyId]);
         setTextCursor(tc);
      }
      break;

   default:
      QTextEdit::keyPressEvent(ke);
      break;
   }
}

QString
Console::processCommand(const QString &cmd)
{
   if(cmd.length() == 0) return "";

   QScriptValue result = scriptEngine.evaluate(cmd);

   history.push_front(cmd.trimmed());

   return result.toString();
}

void
Console::writeLine(const QString &line)
{
   // Get the cursor
   QTextCursor tc = textCursor();

   // Move to the last block
   while(tc.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor))
   {
   }

   // Move to the end of the block
   tc.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);

   // If the last block has a non-empty line - insert a new block
   if(tc.block().length() > 1)
   {
      tc.insertBlock();
   }

   // Output the line
   tc.insertText(line);

   // Insert a new block
   tc.insertBlock();

   // Move the cursor
   setTextCursor(tc);
}